﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Net;
using System.Text;
using TopVisionVideoStream;

public class MultiPartFormUpload
{
    public event EventHandler<UploadProgressChangedEventArgs> UploadProgressChanged;

    public event EventHandler<UploadDataCompletedEventArgs> UploadDataCompleted;

    public class MimePart
    {
        NameValueCollection _headers = new NameValueCollection();
        byte[] _header;

        public NameValueCollection Headers
        {
            get { return _headers; }
        }

        public byte[] Header
        {
            get { return _header; }
        }

        public long GenerateHeaderFooterData(string boundary)
        {
            StringBuilder stringBuilder = new StringBuilder();

            stringBuilder.Append("--");
            stringBuilder.Append(boundary);
            stringBuilder.AppendLine();
            foreach (string key in _headers.AllKeys)
            {
                stringBuilder.Append(key);
                stringBuilder.Append(": ");
                stringBuilder.AppendLine(_headers[key]);
            }
            stringBuilder.AppendLine();

            _header = Encoding.UTF8.GetBytes(stringBuilder.ToString());

            return _header.Length + Data.Length + 2;
        }

        public Stream Data { get; set; }
    }

    public class UploadResponse
    {
        public UploadResponse(HttpStatusCode httpStatusCode, string responseBody)
        {
            HttpStatusCode = httpStatusCode;
            ResponseBody = responseBody;
        }

        public HttpStatusCode HttpStatusCode { get; set; }

        public string ResponseBody { get; set; }
    }

    public UploadResponse Upload(string url, NameValueCollection requestHeaders, NameValueCollection requestParameters, List<FileInfo> files)
    {
        using (WebClient client = new WebClient())
        {
            client.UploadProgressChanged += (o, e) =>
            {
                UploadProgressChanged?.Invoke(o, e);
            };

            client.UploadDataCompleted += (o, e) =>
            {
                UploadDataCompleted?.Invoke(o, e);
            };

            List<MimePart> mimeParts = new List<MimePart>();

            try
            {
                foreach (string key in requestHeaders.AllKeys)
                {
                    client.Headers.Add(key, requestHeaders[key]);
                }

                foreach (string key in requestParameters.AllKeys)
                {
                    MimePart part = new MimePart();

                    part.Headers["Content-Disposition"] = "form-data; name=\"" + key + "\"";
                    part.Data = new MemoryStream(Encoding.UTF8.GetBytes(requestParameters[key]));

                    mimeParts.Add(part);
                }

                foreach (FileInfo file in files)
                {
                    MimePart part = new MimePart();
                    string name = file.Extension.Substring(1);
                    string fileName = file.Name;

                    part.Headers["Content-Disposition"] = "form-data; name=\"" + name + "\"; filename=\"" + fileName + "\"";
                    part.Headers["Content-Type"] = "application/octet-stream";

                    part.Data = new MemoryStream(File.ReadAllBytes(file.FullName));

                    mimeParts.Add(part);
                }

                string boundary = "----------" + DateTime.Now.Ticks.ToString("x");
                client.Headers.Add(HttpRequestHeader.ContentType, "multipart/form-data; boundary=" + boundary);

                long contentLength = 0;

                byte[] _footer = Encoding.UTF8.GetBytes("--" + boundary + "--\r\n");

                foreach (MimePart mimePart in mimeParts)
                {
                    contentLength += mimePart.GenerateHeaderFooterData(boundary);
                }

                byte[] buffer = new byte[8192];
                byte[] afterFile = Encoding.UTF8.GetBytes("\r\n");
                int read;

                using (MemoryStream memoryStream = new MemoryStream())
                {
                    foreach (MimePart mimePart in mimeParts)
                    {
                        memoryStream.Write(mimePart.Header, 0, mimePart.Header.Length);

                        while ((read = mimePart.Data.Read(buffer, 0, buffer.Length)) > 0)
                            memoryStream.Write(buffer, 0, read);

                        mimePart.Data.Dispose();

                        memoryStream.Write(afterFile, 0, afterFile.Length);
                    }

                    memoryStream.Write(_footer, 0, _footer.Length);
                    byte[] responseBytes = client.UploadData(url, memoryStream.ToArray());
                    string responseString = Encoding.UTF8.GetString(responseBytes);
                    return new UploadResponse(HttpStatusCode.OK, responseString);
                }
            }
            catch (Exception ex)
            {
                foreach (MimePart part in mimeParts)
                    if (part.Data != null)
                        part.Data.Dispose();

                if (ex.GetType().Name == "WebException")
                {
                    WebException webException = (WebException)ex;

                    HttpWebResponse response = (HttpWebResponse)webException.Response;

                    string responseString = webException.Message;

                    HttpStatusCode code = HttpStatusCode.NotFound;

                    if (response != null)
                    {
                        using (StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
                        {
                            responseString = reader.ReadToEnd();
                        }

                        code = response.StatusCode;
                    }

                    return new UploadResponse(code, responseString);
                }
                else
                {
                    throw;
                }
            }
        }
    }

    public void UploadAsync(string url, NameValueCollection requestHeaders, NameValueCollection requestParameters, FileInfo file)
    {
        using (WebClient client = new WebClient())
        {
            client.UploadProgressChanged += (o, e) =>
            {
                UploadProgressChanged?.Invoke(o, e);
            };

            client.UploadDataCompleted += (o, e) =>
            {
                UploadDataCompleted?.Invoke(o, e);
            };

            List<MimePart> mimeParts = new List<MimePart>();

            try
            {
                foreach (string key in requestHeaders.AllKeys)
                {
                    client.Headers.Add(key, requestHeaders[key]);
                }

                foreach (string key in requestParameters.AllKeys)
                {
                    MimePart part = new MimePart();

                    part.Headers["Content-Disposition"] = "form-data; name=\"" + key + "\"";

                    part.Data = new MemoryStream(Encoding.UTF8.GetBytes(requestParameters[key]));

                    mimeParts.Add(part);
                }

                //foreach (FileInfo file in files)
                //{
                //    MimePart part = new MimePart();
                //    string name = file.Name;
                //    string fileName = file.FullName;

                //    part.Headers["Content-Disposition"] = "form-data; name=\"" + name + "\"; filename=\"" + fileName + "\"";
                //    part.Headers["Content-Type"] = FileContentType.GetMimeType(name);

                //    part.Data = new MemoryStream(File.ReadAllBytes(file.FullName));

                //    mimeParts.Add(part);
                //}

                MimePart partFile = new MimePart();
                //string name = "file";
                string fileName = file.Name;

                partFile.Headers["Content-Disposition"] = "form-data; name=\"file\"; filename=\"" + fileName + "\"";
                partFile.Headers["Content-Type"] = FileContentType.GetMimeType(Path.GetExtension(file.Name));

                partFile.Data = new MemoryStream(File.ReadAllBytes(file.FullName));

                mimeParts.Add(partFile);

                string boundary = "----------" + DateTime.Now.Ticks.ToString("x");
                client.Headers.Add(HttpRequestHeader.ContentType, "multipart/form-data; boundary=" + boundary);

                long contentLength = 0;

                byte[] _footer = Encoding.UTF8.GetBytes("--" + boundary + "--\r\n");

                foreach (MimePart mimePart in mimeParts)
                {
                    contentLength += mimePart.GenerateHeaderFooterData(boundary);
                }

                byte[] buffer = new byte[8192];
                byte[] afterFile = Encoding.UTF8.GetBytes("\r\n");
                int read;

                using (MemoryStream memoryStream = new MemoryStream())
                {
                    foreach (MimePart mimePart in mimeParts)
                    {
                        memoryStream.Write(mimePart.Header, 0, mimePart.Header.Length);

                        while ((read = mimePart.Data.Read(buffer, 0, buffer.Length)) > 0)
                            memoryStream.Write(buffer, 0, read);

                        mimePart.Data.Dispose();

                        memoryStream.Write(afterFile, 0, afterFile.Length);
                    }

                    memoryStream.Write(_footer, 0, _footer.Length);
                    client.UploadDataAsync(new Uri(url), memoryStream.ToArray());
                    //string responseString = Encoding.UTF8.GetString(responseBytes);
                    //return new UploadResponse(HttpStatusCode.OK, responseString);
                    //return client;
                }
            }
            catch (Exception ex)
            {
                foreach (MimePart part in mimeParts)
                    if (part.Data != null)
                        part.Data.Dispose();

                if (ex.GetType().Name == "WebException")
                {
                    WebException webException = (WebException)ex;
                    HttpWebResponse response = (HttpWebResponse)webException.Response;
                    string responseString;

                    using (StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
                    {
                        responseString = reader.ReadToEnd();
                    }

                    //return null;
                }
                else
                {
                    throw;
                }
            }
        }
    }
}
